/* Copyright (C) 2001-2015 Peter Selinger.
 *  This file is part of Potrace. It is free software and it is covered
 *  by the GNU General Public License. See the file COPYING for details. */


#ifndef _PS_LISTS_H
#define _PS_LISTS_H

/* here we define some general list macros. Because they are macros,
 *  they should work on any datatype with a "->next" component. Some of
 *  them use a "hook". If elt and list are of type t* then hook is of
 *  type t**. A hook stands for an insertion point in the list, i.e.,
 *  either before the first element, or between two elements, or after
 *  the last element. If an operation "sets the hook" for an element,
 *  then the hook is set to just before the element. One can insert
 *  something at a hook. One can also unlink at a hook: this means,
 *  unlink the element just after the hook. By "to unlink", we mean the
 *  element is removed from the list, but not deleted. Thus, it and its
 *  components still need to be freed. */

/* Note: these macros are somewhat experimental. Only the ones that
 *  are actually *used* have been tested. So be careful to test any
 *  that you use. Looking at the output of the preprocessor, "gcc -E"
 *  (possibly piped though "indent"), might help too. Also: these
 *  macros define some internal (local) variables that start with
 *  "_". */

/* we enclose macro definitions whose body consists of more than one
 *  statement in MACRO_BEGIN and MACRO_END, rather than '{' and '}'.  The
 *  reason is that we want to be able to use the macro in a context
 *  such as "if (...) macro(...); else ...". If we didn't use this obscure
 *  trick, we'd have to omit the ";" in such cases. */

#define MACRO_BEGIN do {
#define MACRO_END   } \
    while( 0 )

/* ---------------------------------------------------------------------- */
/* macros for singly-linked lists */

/* traverse list. At the end, elt is set to NULL. */
#define list_forall( elt, list ) for( elt = list; elt!=NULL; elt = elt->next )

/* set elt to the first element of list satisfying boolean condition
 *  c, or NULL if not found */
#define list_find( elt, list, c ) \
    MACRO_BEGIN list_forall( elt, list ) if( c ) \
        break; \
    MACRO_END

/* like forall, except also set hook for elt. */
#define list_forall2( elt, list, hook ) \
    for( elt = list, hook = &list; elt!=NULL; hook = &elt->next, elt = elt->next )

/* same as list_find, except also set hook for elt. */
#define list_find2( elt, list, c, hook ) \
    MACRO_BEGIN list_forall2( elt, list, hook ) if( c ) \
        break; \
    MACRO_END

/* same, except only use hook. */
#define _list_forall_hook( list, hook ) \
    for( hook = &list; *hook!=NULL; hook = &(*hook)->next )

/* same, except only use hook. Note: c may only refer to *hook, not elt. */
#define _list_find_hook( list, c, hook ) \
    MACRO_BEGIN _list_forall_hook( list, hook ) if( c ) \
        break; \
    MACRO_END

/* insert element after hook */
#define list_insert_athook( elt, hook ) \
    MACRO_BEGIN elt->next = *hook; *hook = elt; MACRO_END

/* insert element before hook */
#define list_insert_beforehook( elt, hook ) \
    MACRO_BEGIN elt->next = *hook; *hook = elt; hook = &elt->next; MACRO_END

/* unlink element after hook, let elt be unlinked element, or NULL.
 *  hook remains. */
#define list_unlink_athook( list, elt, hook ) \
    MACRO_BEGIN \
    elt = hook ? *hook : NULL; if( elt ) { *hook = elt->next; elt->next = NULL; } \
    MACRO_END

/* unlink the specific element, if it is in the list. Otherwise, set
 *  elt to NULL */
#define list_unlink( listtype, list, elt )      \
    MACRO_BEGIN                                 \
    listtype * *_hook;               \
    _list_find_hook( list, *_hook==elt, _hook );  \
    list_unlink_athook( list, elt, _hook );       \
    MACRO_END

/* prepend elt to list */
#define list_prepend( list, elt ) \
    MACRO_BEGIN elt->next = list; list = elt; MACRO_END

/* append elt to list. */
#define list_append( listtype, list, elt )     \
    MACRO_BEGIN                                \
    listtype * *_hook;                          \
    _list_forall_hook( list, _hook ) {}          \
    list_insert_athook( elt, _hook );            \
    MACRO_END

/* unlink the first element that satisfies the condition. */
#define list_unlink_cond( listtype, list, elt, c )     \
    MACRO_BEGIN                                        \
    listtype * *_hook;                  \
    list_find2( elt, list, c, _hook );                   \
    list_unlink_athook( list, elt, _hook );              \
    MACRO_END

/* let elt be the nth element of the list, starting to count from 0.
 *  Return NULL if out of bounds.   */
#define list_nth( elt, list, n )                                \
    MACRO_BEGIN                                                 \
    int _x; /* only evaluate n once */                         \
    for( _x = (n), elt = list; _x && elt; _x--, elt = elt->next ) {}   \
    MACRO_END

/* let elt be the nth element of the list, starting to count from 0.
 *  Return NULL if out of bounds.   */
#define list_nth_hook( elt, list, n, hook )                     \
    MACRO_BEGIN                                                 \
    int _x; /* only evaluate n once */                         \
    for( _x = (n), elt = list, hook = &list; _x && elt; \
         _x--, hook = &elt->next, elt = elt->next ) {}   \
    MACRO_END

/* set n to the length of the list */
#define list_length( listtype, list, n )                   \
    MACRO_BEGIN                                            \
    listtype * _elt;                        \
    n = 0;                           \
    list_forall( _elt, list )                \
    n++;                         \
    MACRO_END

/* set n to the index of the first element satisfying cond, or -1 if
 *  none found. Also set elt to the element, or NULL if none found. */
#define list_index( list, n, elt, c )                      \
    MACRO_BEGIN                        \
    n = 0;                           \
    list_forall( elt, list ) {               \
        if( c ) \
            break;                    \
        n++;                         \
    }                          \
    if( !elt )                      \
        n = -1;                        \
    MACRO_END

/* set n to the number of elements in the list that satisfy condition c */
#define list_count( list, n, elt, c )                      \
    MACRO_BEGIN                        \
    n = 0;                           \
    list_forall( elt, list ) {               \
        if( c ) \
            n++;                      \
    }                                                      \
    MACRO_END

/* let elt be each element of the list, unlinked. At the end, set list=NULL. */
#define list_forall_unlink( elt, list ) \
    for( elt = list; elt ? (list = elt->next, elt->next = NULL), 1 : 0; elt = list )

/* reverse a list (efficient) */
#define list_reverse( listtype, list )            \
    MACRO_BEGIN                   \
    listtype * _list1 = NULL, *elt;          \
    list_forall_unlink( elt, list )         \
    list_prepend( _list1, elt );          \
    list = _list1;                \
    MACRO_END

/* insert the element ELT just before the first element TMP of the
 *  list for which COND holds. Here COND must be a condition of ELT and
 *  TMP.  Typical usage is to insert an element into an ordered list:
 *  for instance, list_insert_ordered(listtype, list, elt, tmp,
 *  elt->size <= tmp->size).  Note: if we give a "less than or equal"
 *  condition, the new element will be inserted just before a sequence
 *  of equal elements. If we give a "less than" condition, the new
 *  element will be inserted just after a list of equal elements.
 *  Note: it is much more efficient to construct a list with
 *  list_prepend and then order it with list_merge_sort, than to
 *  construct it with list_insert_ordered. */
#define list_insert_ordered( listtype, list, elt, tmp, cond ) \
    MACRO_BEGIN                                               \
    listtype * *_hook;                                         \
    _list_find_hook( list, ( tmp = *_hook, (cond) ), _hook );       \
    list_insert_athook( elt, _hook );                           \
    MACRO_END

/* sort the given list, according to the comparison condition.
 *  Typical usage is list_sort(listtype, list, a, b, a->size <
 *  b->size).  Note: if we give "less than or equal" condition, each
 *  segment of equal elements will be reversed in order. If we give a
 *  "less than" condition, each segment of equal elements will retain
 *  the original order. The latter is slower but sometimes
 *  prettier. Average running time: n*n/2. */
#define list_sort( listtype, list, a, b, cond )            \
    MACRO_BEGIN                                            \
    listtype * _newlist = NULL;                               \
    list_forall_unlink( a, list )                            \
    list_insert_ordered( listtype, _newlist, a, b, cond ); \
    list = _newlist;                                       \
    MACRO_END

/* a much faster sort algorithm (merge sort, n log n worst case). It
 *  is required that the list type has an additional, unused next1
 *  component. Note there is no curious reversal of order of equal
 *  elements as for list_sort. */

#define list_mergesort( listtype, list, a, b, cond )              \
    MACRO_BEGIN                               \
    listtype * _elt, **_hook1;                     \
                                    \
    for( _elt = list; _elt; _elt = _elt->next1 ) {         \
        _elt->next1 = _elt->next;                       \
        _elt->next  = NULL;                          \
    }                                 \
    do {                                                  \
        _hook1 = &(list);                               \
        while( (a = *_hook1) != NULL && (b = a->next1) != NULL ) {  \
            _elt = b->next1;                          \
            _list_merge_cond( listtype, a, b, cond, *_hook1 );          \
            _hook1  = &( (*_hook1)->next1 );                 \
            *_hook1 = _elt;                               \
        }                                   \
    } \
    while( _hook1 != &(list) );                                  \
    MACRO_END

/* merge two sorted lists. Store result at &result */
#define _list_merge_cond( listtype, a, b, cond, result )   \
    MACRO_BEGIN                                            \
    listtype * *_hook;                  \
    _hook = &(result);                     \
    while( 1 ) {                                            \
        if( a==NULL ) {                  \
            *_hook = b;                   \
            break;                        \
        } \
        else if( b==NULL ) {               \
            *_hook = a;                   \
            break;                        \
        } \
        else if( cond ) {                  \
            *_hook  = a;                   \
            _hook   = &(a->next);               \
            a = a->next;                  \
        } \
        else {                        \
            *_hook  = b;                   \
            _hook   = &(b->next);               \
            b = b->next;                  \
        }                           \
    }                          \
    MACRO_END

/* ---------------------------------------------------------------------- */
/* macros for doubly-linked lists */

#define dlist_append( head, end, elt )                    \
    MACRO_BEGIN                                            \
    elt->prev = end;                   \
    elt->next = NULL;                  \
    if( end ) {                         \
        end->next = elt;                     \
    } \
    else {                           \
        head = elt;                      \
    }                              \
    end = elt;                         \
    MACRO_END

/* let elt be each element of the list, unlinked. At the end, set list=NULL. */
#define dlist_forall_unlink( elt, head, end ) \
    for( elt = head; \
         elt ? (head = elt->next, elt->next = NULL, elt->prev = NULL), 1 : (end = NULL, 0); \
         elt = head )

/* unlink the first element of the list */
#define dlist_unlink_first( head, end, elt )               \
    MACRO_BEGIN                                \
    elt = head;                        \
    if( head ) {                        \
        head = head->next;                   \
        if( head ) {                      \
            head->prev = NULL;                 \
        } \
        else {                         \
            end = NULL;                    \
        }                            \
        elt->prev = NULL;                    \
        elt->next = NULL;                    \
    }                          \
    MACRO_END

#endif /* _PS_LISTS_H */
